﻿using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;

namespace Foundation
{
    public class TypeHelper
    {
        public static object GenerateAnonimousObject(Dictionary<string, object> properties)
        {
            var assemblyName = new AssemblyName("DynamicAssemblyExample");
            var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave);
            var moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name, assemblyName.Name + ".dll");
            var typeBuilder = moduleBuilder.DefineType("MyDynamicType", TypeAttributes.Public | TypeAttributes.Serializable);

            foreach (var property in properties)
            {
                var propertyType = property.Value == null ? typeof (object) : property.Value.GetType();
                var fieldBuilder = typeBuilder.DefineField("m_" + properties.Keys, propertyType, FieldAttributes.Private);
                var propertyBuilder = typeBuilder.DefineProperty(property.Key, PropertyAttributes.HasDefault, propertyType, null);
                const MethodAttributes getSetAttr = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig;
                var getter = typeBuilder.DefineMethod("get_" + property.Key, getSetAttr, propertyType, Type.EmptyTypes);
                var setter = typeBuilder.DefineMethod("set_" + property.Key, getSetAttr, null, new[] { propertyType });

                var getterIlGenerator = getter.GetILGenerator();
                getterIlGenerator.Emit(OpCodes.Ldarg_0);
                getterIlGenerator.Emit(OpCodes.Ldfld, fieldBuilder);
                getterIlGenerator.Emit(OpCodes.Ret);

                var setterIlGenerator = setter.GetILGenerator();
                setterIlGenerator.Emit(OpCodes.Ldarg_0);
                setterIlGenerator.Emit(OpCodes.Ldarg_1);
                setterIlGenerator.Emit(OpCodes.Stfld, fieldBuilder);
                setterIlGenerator.Emit(OpCodes.Ret);
 
                propertyBuilder.SetGetMethod(getter);
                propertyBuilder.SetSetMethod(setter);
            }
   
            var type = typeBuilder.CreateType();
            var obj = Activator.CreateInstance(type);
            foreach (var property in type.GetProperties())
            {
                property.SetValue(obj, properties[property.Name], BindingFlags.Default, null, null, null);
            }

            return obj;
        }
    }
}
